﻿// Jinx AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Formality.FormalSystem;
using Core.Parsing.ContextFree;

namespace Formality.FormalSystem.Parser
{
    public class GrammarBuilder
    {
        public GrammarBuilder(Terminals Terminals, PrecedenceRules PrecedenceRules)
        {
            this.PrecedenceRules = PrecedenceRules;

            var T = Terminals;

            var Proposition = new Nonterminal<Proposition>("proposition");
            var AntecedentList = new Nonterminal<List<Proposition>>("antecedent-list");
            var Expression = new Nonterminal<Proposition>("expression");
            var OperatorList = new Nonterminal<InfixOperatorList>("operator-list");
            var UnaryOperator = new Nonterminal<Proposition>("unary-operator");
            var Term = new Nonterminal<Proposition>("term");

            var Variable = new Nonterminal<Variable>("variable");
            var VariableList = new Nonterminal<List<Variable>>("variable-list");

            Proposition.add(Expression, p => p);
            Proposition.add(AntecedentList, ConstructAxiom);

            AntecedentList.add(AntecedentList, T.COMMA, Expression, (a, c, p) => { a.Add(p); return a; });
            AntecedentList.add(Expression, T.COMMA, Expression, (p1, c, p2) => new List<Proposition>(new[] { p1, p2 }));

            Expression.add(OperatorList, InfixOperator);
            Expression.add(UnaryOperator, u => u);

            OperatorList.add(OperatorList, T.OPERATOR, UnaryOperator, (l, op, u) => l.Add(op, u));
            OperatorList.add(UnaryOperator, T.OPERATOR, UnaryOperator, InfixOperatorList.New);

            UnaryOperator.add(T.BINDER, Variable, UnaryOperator, (b, v, s) => b.Binder(v, s));
            UnaryOperator.add(T.OPERATOR, UnaryOperator, (s, o) => s.Operator(o));
            UnaryOperator.add(Term, a => a);

            Term.add(Variable, v => new Proposition(v));
            Term.add(Variable, Variable, (f, a) => new Proposition(f, a));
            Term.add(Variable, T.LEFT, VariableList, T.RIGHT, (f, l, a, r) => new Proposition(f, a));
            Term.add(Variable, T.BANG, Variable, (f, b, a) => new Proposition(f, new[] { a }, true));
            Term.add(Variable, T.BANG, T.LEFT, VariableList, T.RIGHT, (f, b, l, a, r) => new Proposition(f, a, true));
            Term.add(T.LEFT, Expression, T.RIGHT, (l, p, r) => p);

            Variable.add(T.IDENT, i => i);

            VariableList.add(VariableList, T.COMMA, Variable, (l, c, v) => { l.Add(v); return l; });
            VariableList.add(Variable, v => new List<Variable>(new[] { v }));
            VariableList.add(() => new List<Variable>());

            Grammar = new Grammar<Proposition>(Proposition);
        }

        private static Proposition ConstructAxiom(ICollection<Proposition> List)
        {
            var Final = new BinaryOperator(List.Last(), Operators.RULE);

            var Operands = List.Take(List.Count - 1);
            Operands = Operands.Concat(new[] { Final.Left, Final.Right });

            return Operators.RULE.Operator(Operands);
        }

        private Proposition InfixOperator(InfixOperatorList OpList)
        {
            return PrecedenceRules.Parse(OpList.Operands, OpList.Operators);
        }

        public readonly Grammar<Proposition> Grammar;

        private class InfixOperatorList
        {
            public static InfixOperatorList New(Proposition P1, string Operator, Proposition P2)
            {
                var N = new InfixOperatorList
                {
                    Operands = new List<Proposition>(),
                    Operators = new List<string>()
                };

                N.Operands.Add(P1);
                N.Operators.Add(Operator);
                N.Operands.Add(P2);

                return N;
            }

            public InfixOperatorList Add(string Operator, Proposition P)
            {
                Operators.Add(Operator);
                Operands.Add(P);
                return this;
            }

            public override string ToString()
            {
                var Buffer = new StringBuilder("[");

                var Operand = Operands.GetEnumerator();
                foreach (var Op in Operators)
                {
                    Operand.MoveNext();
                    Buffer.AppendFormat("{0} {1} ", Operand.Current, Op);
                }

                Buffer.Append(Operand.Current.ToString());

                Buffer.Append("]");
                return Buffer.ToString();
            }

            public List<Proposition> Operands;
            public List<string> Operators;
        }

        private readonly PrecedenceRules PrecedenceRules;
    }
}
